import { ApiMsgEnum, EntityTypeEnum, IClientInput, IMsgClientSync, InputTypeEnum, IState, toFixed } from '../Common'
import { Connection } from '../Core'
import { Player } from './Player'
import { PlayerManager } from './PlayerManager'
import { RoomManager } from './RoomManager'

const MAP_WIDTH = 960 // 地图宽
const MAP_HEIGHT = 640 // 地图高

export class Room {
  id: number
  players: Set<Player> = new Set()

  // 上次同步时刻
  private lastTime: number
  private timers: NodeJS.Timer[] = []
  private pendingInput: IClientInput[] = [] // 待同步的指令集
  // 玩家上次同步帧id集合
  private lastPlayerFrameIdMap: Map<number, number> = new Map()

  constructor(rid: number) {
    this.id = rid
  }

  join(uid: number) {
    const player = PlayerManager.Instance.idMapPlayer.get(uid)
    if (player) {
      player.rid = this.id
      this.players.add(player)
    }
  }
  leave(uid: number) {
    const player = PlayerManager.Instance.idMapPlayer.get(uid)
    if (player) {
      player.rid = null
      player.connection.unListenMsg(ApiMsgEnum.MsgClientSync, this.getClientMsg, this)
      this.players.delete(player)
      // 空房自动关闭
      if (!this.players.size) {
        RoomManager.Instance.closeRoom(this.id)
      }
    }
  }

  close() {
    this.timers.forEach(t => clearInterval(t))
    this.players.clear()
  }

  // 本房间玩家同步
  async() {
    for (const player of this.players) {
      player.connection.sendMsg(ApiMsgEnum.MsgRoom, {
        room: RoomManager.Instance.getRoomView(this),
      })
    }
  }

  // 游戏开始
  start() {
    // 初始化战场
    const rand = (min: number, max: number) => Math.floor((max - min + 1) * Math.random()) + min
    const state: IState = {
      // 地图中角色信息集合
      actors: [...this.players].map((player, index) => ({
        id: player.id,
        nickname: player.nickname,
        hp: 100,
        type: Math.random() > 0.5 ? EntityTypeEnum.Actor1 : EntityTypeEnum.Actor2,
        weaponType: Math.random() > 0.5 ? EntityTypeEnum.Weapon1 : EntityTypeEnum.Weapon2,
        bulletType: Math.random() > 0.5 ? EntityTypeEnum.Bullet1 : EntityTypeEnum.Bullet2,
        position: { x: rand(-MAP_WIDTH / 2, MAP_WIDTH / 2), y: rand(-MAP_HEIGHT / 2, MAP_HEIGHT / 2) },
        direction: { x: Math.random() > 0.5 ? 1 : -1, y: 0 },
      })),
      // 子弹集合
      bullets: [],
      // 子弹id
      nextBulletID: 1,
      seed: 1,
    }
    for (const player of this.players) {
      player.connection.sendMsg(ApiMsgEnum.MsgGameStart, {
        state,
      })
      // 房间内玩家开始 接受客户端帧同步指令
      player.connection.listenMsg(ApiMsgEnum.MsgClientSync, this.getClientMsg, this)
    }

    // 间隔100毫秒同步
    const timer1 = setInterval(() => {
      this.sendServerMsg()
    }, 100)
    // 时间流逝
    const timer2 = setInterval(() => {
      this.timePast()
    }, 16)

    this.timers = [timer1, timer2]
  }

  // 缓存客户端帧同步输入
  getClientMsg(connection: Connection, { input, frameID }: IMsgClientSync) {
    this.lastPlayerFrameIdMap.set(connection.playerID, frameID)
    this.pendingInput.push(input)
  }

  // 帧同步输入给客户端
  sendServerMsg() {
    const inputs = this.pendingInput
    if (inputs.length == 0) return
    this.pendingInput = []
    for (const player of this.players) {
      player.connection.sendMsg(ApiMsgEnum.MsgServerSync, {
        lastFrameID: this.lastPlayerFrameIdMap.get(player.id) || 0,
        inputs,
      })
    }
  }

  // 时间流逝
  timePast() {
    const now = process.uptime()
    const dt = now - (this.lastTime ?? now)
    this.pendingInput.push({ type: InputTypeEnum.TimePast, dt: toFixed(dt) })
    this.lastTime = now
  }
}
